/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_httpd.htm
 *
 * Zhiqim Httpd is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.httpd;

import java.util.ArrayList;
import java.util.List;

import org.zhiqim.kernel.model.objects.Int;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.kernel.util.Urls;
import org.zhiqim.kernel.util.Validates;

/**
 * HTTP请求响应抽象类，把响应相关的处理的放在该类中
 *
 * @version v1.0.0 @author zouzhigang 2016-7-15 新建与整理
 */
public abstract class HttpRequestResponse extends HttpRequestValidate
{
    private boolean isReturn;           //是否转向，=false时[tempalte|forward|redirect]无效
    
    //用于在请求中设置响应属性
    private int responseStatus;         //601-603表示重定向
    
    //三项为Action配置的转向控制字段
    private String forward;             //转向到新的path
    private String redirect;            //重定向到新path
    private String view;                //加载视图信息
    private String include;             //加载视图嵌套信息
    private String executeMethod;       //内部通过execute转向到该方法
    private String alertMsg;            //提示信息
    
    //一项为转向时的可选配置字段
    private List<String[]> paramList;   //重定向参数列表
    
    /**
     * 设置响应配置信息
     * 
     * @param view          转向到视图页
     * @param include       转向到视图页的嵌套地址
     * @param forward       转向到新的路径
     * @param redirect      重定向到新的路径
     * @param method        执行类的方法
     * @param alert         执行后的警告信息
     */
    public void setResponseConfig(String view, String include, String forward, String redirect, String method, String alert)
    {
        this.isReturn = !Validates.isEmptyBlank(view) || !Validates.isEmptyBlank(forward) || !Validates.isEmptyBlank(redirect);
        
        this.view = view;
        this.include = include;
        this.forward = forward;
        this.redirect = redirect;
        
        this.executeMethod = method;
        this.alertMsg = alert;
    }
    
    /**********************************************/
    //加上标准SET方法，用于运行时修改设置
    /**********************************************/
    
    /** 修改为是否转向 */
    public void setReturn(boolean isReturn)
    {
        this.isReturn = isReturn;
    }
    
    /** 修改视图页和嵌套地址，和转向页互斥 */
    public void setView(String view, String include)
    {
        this.responseStatus = 0;
        
        this.view = view;
        this.include = include;
        this.forward = null;
        this.redirect = null;
        this.isReturn = true;
    }
    
    /** 修改为是否内部转向 */
    public void setForward(String forwardTo)
    {
        this.responseStatus = 0;
        
        this.forward = forwardTo;
        this.view = null;
        this.redirect = null;
        this.isReturn = true;
    }
    
    /** 修改重定向，和视图页互斥 */
    public void setRedirect(String redirect)
    {
        this.responseStatus = _602_REDIRECT_;
        
        setRedirectInner(redirect);
    }
    
    /** 修改重定向，和视图页互斥，但不改变responseStatus值 */
    public void setRedirectInner(String redirect)
    {
        this.redirect = redirect;
        this.view = null;
        this.forward = null;
        this.isReturn = true;
    }
    
    /** 增加参数，内转向和重定向时有效 */
    public void addParam(String key, Object value)
    {
        if (value == null)
            value = "";
        
        if (paramList == null)
            paramList = new ArrayList<>(2);
            
        paramList.add(new String[]{key, String.valueOf(value)});
    }
    
    /** 设置警告信息 */
    public void setAlertMsg(String alert)
    {
        this.alertMsg = alert;
    }
    
    /*******************************************************************************/
    //转向控制字段相关属性
    /*******************************************************************************/

    /** 获取Action在execute中转向的方法名 */
    public String getExecuteMethod()
    {
        return executeMethod;
    }
    
    /** 获取转向定制的提示信息 */
    public String getAlertMsg()
    {
        return alertMsg;
    }
    
    /** 
     * 检查是否转向，两种情况<br><br>
     * 
     * 1、当设置成强制不转向时，即不转向<br>
     * 2、当为转向时，检查视图页和转向页是否存在，存在一个即转向，否则不转向<br>
      */
    public boolean isReturn()
    {
        return isReturn?(Validates.isNotEmpty(view) || Validates.isNotEmpty(forward) || Validates.isNotEmpty(redirect)):false;
    }
    
    /** 获取视图信息 */
    public String getView()
    {
        return view;
    }
    
    /** 获取视图信息嵌套信息 */
    public String getInclude()
    {
        return include;
    }

    /** 获取内转向地址 */
    public String getForward(String encoding)
    {
        if (Validates.isEmpty(paramList))
            return forward;
        
        //加参数
        for (String[] param : paramList)
        {
            String value = Urls.encode(param[1], encoding);
            forward = Urls.add(forward, param[0], value);
        }
        return forward;
    }
    
    /** 获取重定向内部定义的地址 */
    public String getRedirectInner()
    {
        return redirect;
    }
    
    /** 获取重定向地址 */
    public String getRedirect(String encoding)
    {
        if (redirect == null)
            return redirect;
        
        if (_REDIRECT_HISTORY_.equals(redirect))
            return redirect;
            
        if (_REDIRECT_CLOSE_WINDOW_.equals(redirect))
            return redirect;
        
        if (_REDIRECT_CLOSE_DIALOG_.equals(redirect))
            return redirect;
        
        if (Strings.startsWith(redirect, "javascript:"))
            return redirect;
        
        if (Validates.isEmpty(paramList))
            return redirect;
        
        //加参数
        for (String[] param : paramList)
        {
            String value = Urls.encode(param[1], encoding);
            redirect = Urls.add(redirect, param[0], value);
        }
        return redirect;
    }
    
    /**********************************************/
    //加上四个常用特性方法，用于运行时修改设置
    /**********************************************/
    
    /** 增加一个返回上一页快捷方式 */
    public void returnHistory()
    {
        setRedirect(_REDIRECT_HISTORY_);
    }
    
    /** 增加一个错误返回上一页并提示错误的快捷方式 */
    public void returnHistory(String errorMsg)
    {
        setRedirect(_REDIRECT_HISTORY_);
        setAlertMsg(errorMsg);
    }
    
    /** 增加一个弹出窗口时关闭快捷方式 */
    public void returnCloseWindow()
    {
        setRedirect(_REDIRECT_CLOSE_WINDOW_);
    }
    
    /** 增加一个弹出窗口时关闭并提示错误的快捷方式 */
    public void returnCloseWindow(String errorMsg)
    {
        setRedirect(_REDIRECT_CLOSE_WINDOW_);
        setAlertMsg(errorMsg);
    }
    
    /** 增加一个弹出对话框时关闭快捷方式 */
    public void returnCloseDialog()
    {
        setRedirect(_REDIRECT_CLOSE_DIALOG_);
    }
    
    /** 增加一个弹出对话框时关闭并提示错误的快捷方式 */
    public void returnCloseDialog(String errorMsg)
    {
        setRedirect(_REDIRECT_CLOSE_DIALOG_);
        setAlertMsg(errorMsg);
    }
    
    /***********************************************/
    //以下判断是否强制要求转向（通过JS转向的）
    /***********************************************/
    
    /** 
     * 是否强制转向
     * 
     * 1.如果有失败提示或成功提示，需强制转向
     * 2.由业务指定返回上一页或关闭，需强制转向
     */
    public boolean isForceReturn()
    {
        //要提示信息要求alert
        if (Validates.isNotEmpty(alertMsg))
            return true;
        
        //当要求返回上一页、关闭当前页和关闭当前对话框时需强制转向
        if (_REDIRECT_HISTORY_.equals(redirect) || _REDIRECT_CLOSE_WINDOW_.equals(redirect) || _REDIRECT_CLOSE_DIALOG_.equals(redirect))
            return true;
        
        //当要求执行一段javascript代码时
        if (Strings.startsWith(redirect, "javascript:"))
            return true;
        
        //parent.location和top.location两种必须强制转向
        if (responseStatus == _601_REDIRECT_PARENT_ || responseStatus == _603_REDIRECT_TOP_)
            return true;
        
        //在iframe中的提交，要强制转向
        if (getParameterBoolean(_PARAM_CALL_FRAME_))
            return true;
        
        return false;
    }
    
    /**
     * 获取强制转向类型
     * 
     * 1.返回上一页
     * 2.关闭当前窗口
     * 3.关闭对话框
     * 4.调用Javascript
     * 0.表示正常连接
     * @return int 类型
     */
    public int getForceReturnType()
    {
        //当要求返回上一页、关闭当前页和关闭当前对话框时需强制转向
        if (redirect == null)
            return -1;
        if (_REDIRECT_HISTORY_.equals(redirect))
            return 1;
        else if(_REDIRECT_CLOSE_WINDOW_.equals(redirect))
            return 2;
        else if(_REDIRECT_CLOSE_DIALOG_.equals(redirect))
            return 3;
        else if (Strings.startsWith(redirect, "javascript:"))
            return 4;
        else
            return 0;
    }
    
    /***********************************************************************/
    //从请求中设置响应状态和文本属性
    /***********************************************************************/
    
    /** 获取响应属性码 */
    public int getResponseStatus()
    {
        return responseStatus;
    }
    
    /** 获取响应属性文本 */
    public String getResponseText()
    {
        if (!isResponseRedirect())
        {//不是重定向，直接返回提示信息
            return alertMsg;
        }
        else
        {//重定向则为redirect#alertMsg
            if (Validates.isEmptyBlank(alertMsg))
                return getRedirect(_UTF_8_);
            else
                return getRedirect(_UTF_8_) + "#" + alertMsg;
        }
    }
    
    /** 判断响应属性是不是成功 */
    public boolean isResponseSuccess()
    {
        return responseStatus == _0_SUCCESS_;
    }
    
    /** 判断响应属性是不是重定向 */
    public boolean isResponseRedirect()
    {
        return responseStatus >= _601_REDIRECT_PARENT_ && responseStatus <= _603_REDIRECT_TOP_;
    }
    
    /** 设置响应结果属性，指定状态码但不能小于0，不能填70-699之间已知的状态码 */
    public void setResponseResult(int responseStatus, String responseText)
    {
        if (responseStatus < 0 || (responseStatus >= 70 && responseStatus <= 699))
            throw new RuntimeException("响应状态码不允许在(小于0和100-300和 304-699)之间数");
        
        this.responseStatus = responseStatus;
        setAlertMsg(responseText);
    }
    
    /** 设置响应结果属性，指定状态码但不能小于0，不能填70-699之间已知的状态码 */
    public void setResponseResult(Int result)
    {
        this.setResponseResult(result.code(), result.msg());
    }
    
    /** 设置响应结果属性，状态码为0，表示成功 */
    public void setResponseResult(String responseText)
    {
        this.responseStatus = _0_SUCCESS_;
        setAlertMsg(responseText);
    }
    
    /** 设置响应结果属性，状态码为699，表示通用的失败 */
    public void setResponseError(String responseText)
    {
        this.responseStatus = _699_ERROR_;
        setAlertMsg(responseText);
    }
    
    /** 设置响应父页重定向，状态码为601 */
    public void setRedirectParent(String redirect)
    {
        this.responseStatus = _601_REDIRECT_PARENT_;
        setRedirectInner(redirect);
    }
    
    /** 设置响应顶页重定向 ，状态码为603*/
    public void setRedirectTop(String redirect)
    {
        this.responseStatus = _603_REDIRECT_TOP_;
        setRedirectInner(redirect);
    }
    
    /** 设置响应重定向前提示错误，状态码为602 */
    public void setRedirect(String redirect, String error)
    {
        if (Validates.isEmptyBlank(error)){
            setRedirect(redirect);
            return;
        }
        
        setRedirect(redirect);
        setAlertMsg(error);
    }
    
    /** 设置响应父页重定向前提示错误，状态码为601  */
    public void setRedirectParent(String redirect, String error)
    {
        if (Validates.isEmptyBlank(error)){
            setRedirectParent(redirect);
            return;
        }
        
        this.responseStatus = _601_REDIRECT_PARENT_;
        setRedirectInner(redirect);
        setAlertMsg(error);
    }
    
    /** 设置响应顶页重定向前提示错误，状态码为603  */
    public void setRedirectTop(String redirect, String error)
    {
        if (Validates.isEmptyBlank(error)){
            setRedirectTop(redirect);
            return;
        }
        
        this.responseStatus = _603_REDIRECT_TOP_;
        setRedirectInner(redirect);
        setAlertMsg(error);
    }
    
    protected void destroy()
    {
        super.destroy();
        
        if (paramList != null)
        {
            paramList.clear();
            paramList = null;
        }
        
        forward = null;
        redirect = null;
        view = null;
        executeMethod = null;
        alertMsg = null;
    }
}
